#include "evolution.h"

Evolution::Evolution(   Input& myInput,
                        unsigned int& myAmountOfEvolutionElements,
                        unsigned int& myAmountOfFormulasPerElement,
                        unsigned int& mySizeOfNewCreatedFormulas)
                    :   input(myInput),
                        amountOfEvolutionElements(myAmountOfEvolutionElements),
                        amountOfFormulasToCreatePerElement(myAmountOfFormulasPerElement),
                        sizeOfNewCreatedFormulas(mySizeOfNewCreatedFormulas) {
    assert(myAmountOfEvolutionElements > 0);
    assert(myAmountOfFormulasPerElement > 0);
    evoElements=new EvolutionElement*[myAmountOfEvolutionElements];
    for(unsigned int i=myAmountOfEvolutionElements; i--;)
        evoElements[i]=new EvolutionElement(amountOfFormulasToCreatePerElement, sizeOfNewCreatedFormulas, input);
}

Evolution::~Evolution() {
    for(int i=amountOfEvolutionElements; i--;)
        delete evoElements[i];
    delete [] evoElements;
}

void Evolution::run(unsigned int& amountOfRounds, bool output) {
    unsigned int roundsLeft=amountOfRounds;
    const unsigned int fiveHundredAndTwentyTwo = 522;
    while(roundsLeft && evoElements[0]->getBestFitnessScore()) {
        if(roundsLeft>(fiveHundredAndTwentyTwo-1)) {
            doSomeRounds(fiveHundredAndTwentyTwo, output);
            roundsLeft-=fiveHundredAndTwentyTwo;
        }
        else {
            doSomeRounds(roundsLeft, output);
            roundsLeft=0;
        }
        sort();
        saveBestResult();
        recombine();
    }

    if(output) {
        std::cout<< "\nI finally reached: ";
        std::cout << evoElements[0]->getBestFitnessScore();
        printBestResult();
    }
}

void Evolution::recombine() {
    unsigned int chance;
    if((amountOfEvolutionElements>2) && (amountOfFormulasToCreatePerElement>2)) {
        for(unsigned int i=amountOfEvolutionElements; --i;) {
            chance=rand()%5;
            if(chance==1) {
                delete evoElements[i]->getFormulas()[amountOfFormulasToCreatePerElement-1];
                evoElements[i]->getFormulas()[amountOfFormulasToCreatePerElement-1]=FormulaFactory::simpleRecombination(*(evoElements[0]->getFormulas()[0]), *(evoElements[1]->getFormulas()[0]));
            }
            else if(chance==2) {
                delete evoElements[i]->getFormulas()[amountOfFormulasToCreatePerElement-1];
                evoElements[i]->getFormulas()[amountOfFormulasToCreatePerElement-1]=FormulaFactory::simpleRecombination(*(evoElements[1]->getFormulas()[0]), *(evoElements[2]->getFormulas()[0]));
            }
            else if(chance==3) {
                delete evoElements[i]->getFormulas()[amountOfFormulasToCreatePerElement-1];
                evoElements[i]->getFormulas()[amountOfFormulasToCreatePerElement-1]=evoElements[0]->getFormulas()[0]->copy();
            }
            else if(chance==4) {
                delete evoElements[i]->getFormulas()[amountOfFormulasToCreatePerElement-1];
                evoElements[i]->getFormulas()[amountOfFormulasToCreatePerElement-1]=evoElements[1]->getFormulas()[0]->copy();
            }
        }
    }
}

void Evolution::saveBestResult() {
    std::ofstream myBestResultFile;
    myBestResultFile.open("bestResult.txt", std::ios::out | std::ios::trunc);
    myBestResultFile << "This is the best result reached so fare.\n";
    std::string& deleteMe = evoElements[0]->getFormulas()[0]->toHumanString();
    myBestResultFile << deleteMe;
    delete &deleteMe;
    myBestResultFile.close();
}

void Evolution::printBestResult() {
    std::string& deleteMe = evoElements[0]->getFormulas()[0]->toHumanString();
    std::cout   << "\nThis is the best result reached so fare.\n";
    std::cout   << deleteMe << std::endl;
    delete &deleteMe;
}
